package cn.edu.zstu.overlappingdisappear.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author ZhuMingWei(986990501@qq.com)
 * @Date on 2019/11/10
 * @Version v1.0.0
 * @Company TODO
 * @Describle TODO
 */

public class GridSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable,GestureDetector.OnGestureListener {

    private GestureDetector mGestureDetector;

    private int GRID_HALF_COUNT = 12;
    private int GRID_COUNT = 12;
    private int minX,maxX;
    private int minY,maxY;
    private float centerX;
    private float centerY;
    private float maxDst;
    private float offsetLeft;
    private float offsetTop;
    private float mPadding = 10.0f;
    public int mBolckWidth = 40;
    // SurfaceHolder
    private SurfaceHolder mSurfaceHolder;
    // 画布
    private Canvas mCanvas;
    // 画笔
    private Paint mPaint;
    // 子线程标志位
    private boolean isDrawing;
    //基础图形
    private List<List<Point>> mPaths;
    private int selectIndex = -1;

    private List<Point> orginPath;

    public boolean isDrawGrid = true;
    public boolean isZoom = false;
    private boolean isVisible = true;

    private OnMoveFinishListener mOnMoveFinishListener;

    public interface OnMoveFinishListener{
        void onMoveFinish(Canvas canvas,List<List<Point>> paths);
    }

    public void setOnMoveFinishListener(OnMoveFinishListener listener){
        mOnMoveFinishListener = listener;
    }

    public GridSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        //设置画笔
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setStrokeWidth(5);
        mPaths = new ArrayList<>();
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
    }

    public void addGesture(boolean isAdd){
        if(isAdd)
            mGestureDetector = new GestureDetector(getContext(), this);
        else
            mGestureDetector = null;
    }

    public void addInitStates(List<List<Point>> paths){
        mPaths.clear();
        minX = minY = 10000;
        maxX = maxY = 0;
        for(int i=0;i<paths.size();i++){
            List<Point> path = new ArrayList<>();
            for(int j=0;j<paths.get(i).size();j++){
                if(isZoom)
                    path.add(new Point(paths.get(i).get(j).x,paths.get(i).get(j).y));
                else
                    path.add(new Point(paths.get(i).get(j).x * mBolckWidth,paths.get(i).get(j).y * mBolckWidth));
                if(minX > paths.get(i).get(j).x)
                    minX = paths.get(i).get(j).x;
                if(maxX < paths.get(i).get(j).x)
                    maxX = paths.get(i).get(j).x;
                if(minY > paths.get(i).get(j).y)
                    minY = paths.get(i).get(j).y;
                if(maxY < paths.get(i).get(j).y)
                    maxY = paths.get(i).get(j).y;
            }
            mPaths.add(path);
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {//创建
        isDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//改变

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {//销毁
        isDrawing = false;
    }

    @Override
    public void run() {
        while (isDrawing){
            drawing();
        }
    }

    private void drawing() {
        try {
            if(isZoom) {
                mCanvas = mSurfaceHolder.lockCanvas();
                mCanvas.drawColor(Color.WHITE);
                drawZoomGrid();
                //这里进行内容的绘制
                mCanvas.save();
                //画图形
                if(mPaths.size()>0) {
                    for (int i=0;i<mPaths.size();i++) {
                        Path path = new Path();
                        for(int j=0;j<mPaths.get(i).size();j++){
                            if(j == 0)
                                path.moveTo(offsetLeft + (mPaths.get(i).get(j).x - minX) * mBolckWidth,offsetTop +(mPaths.get(i).get(j).y-minY)*mBolckWidth);
                            else
                                path.lineTo(offsetLeft + (mPaths.get(i).get(j).x - minX) * mBolckWidth,offsetTop + (mPaths.get(i).get(j).y-minY)*mBolckWidth);
                        }
                        path.close();
                        if(i == 0)
                            mCanvas.clipPath(path);
                        else
                            mCanvas.clipPath(path,Region.Op.XOR);
                    }
                }
                mCanvas.drawColor(Color.BLACK);
                mCanvas.restore();
            }else {
                mCanvas = mSurfaceHolder.lockCanvas();
                mCanvas.drawColor(Color.WHITE);
                drawGrid();
                //这里进行内容的绘制
                mCanvas.save();
                //画图形
                if(mPaths.size()>0) {
                    for (int i=0;i<mPaths.size();i++) {
                        Path path = new Path();
                        for(int j=0;j<mPaths.get(i).size();j++){
                            if(j == 0)
                                path.moveTo(offsetLeft + mPaths.get(i).get(j).x,offsetTop + mPaths.get(i).get(j).y);
                            else
                                path.lineTo(offsetLeft + mPaths.get(i).get(j).x,offsetTop + mPaths.get(i).get(j).y);
                        }
                        path.close();
                        if(i == 0)
                            mCanvas.clipPath(path);
                        else
                            mCanvas.clipPath(path,Region.Op.XOR);
                    }
                }
                mCanvas.drawColor(Color.BLACK);
                mCanvas.restore();
            }
        }
        catch (Exception e){

        }
        finally {
            if (mCanvas != null){
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    private void drawGrid() {
        if(null == mCanvas)
            return;
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.LTGRAY);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        //绘制圆形区域
        float left = getLeft();
        float right = getRight();
        float top = getTop();
        float bottom = getBottom();
        float cx = (right-left)/2;
        float cy = (bottom-top)/2;
        float r = right - left > bottom - top? (bottom-top)/2 : (right-left)/2;
        mCanvas.drawCircle(cx, cy, r - mPadding, paint);
        mBolckWidth = (int)((r - mPadding)/GRID_HALF_COUNT);
        maxDst = (r - mPadding)*(r - mPadding);
        offsetLeft = cx;
        offsetTop = cy;
        centerX = cx;
        centerY = cy;
        for(float sx = 0; sx < right / 2;sx = sx + mBolckWidth){
            for(float sy = 0;sy < bottom / 2;sy = sy + mBolckWidth){
                float dst = sx * sx + sy * sy;
                if(dst < maxDst){
                    if(isDrawGrid){
                        paint.setStyle(Paint.Style.FILL);
                        mCanvas.drawPoint(cx - sx,cy - sy ,paint);
                        mCanvas.drawPoint(cx - sx,cy + sy ,paint);
                        mCanvas.drawPoint(cx + sx,cy + sy ,paint);
                        mCanvas.drawPoint(cx + sx,cy - sy ,paint);
                    }
                    if(cx - sx < offsetLeft)
                        offsetLeft = cx - sx;
                    if(cy - sy < offsetTop)
                        offsetTop = cy - sy;
                }
            }
        }
    }

    private void drawZoomGrid() {
        if(null == mCanvas)
            return;
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.LTGRAY);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        //绘制圆形区域
        float left = getLeft();
        float right = getRight();
        float top = getTop();
        float bottom = getBottom();
        float cx = (right-left)/2;
        float cy = (bottom-top)/2;
        float r = right - left > bottom - top? (bottom-top)/2 : (right-left)/2;
        mCanvas.drawCircle(cx, cy, r - mPadding, paint);
        int zr = maxX-minX>maxY-minY?maxX-minX:maxY-minY;
        GRID_COUNT = zr * 2;
        mBolckWidth = (int)((r - mPadding) * 2 /GRID_COUNT);
        maxDst = (r - mPadding)*(r - mPadding);
        offsetLeft = cx;
        offsetTop = cy;
        centerX = cx;
        centerY = cy;
        for(float sx = 0; sx < getRight();sx = sx + mBolckWidth){
            for(float sy = 0;sy < getBottom();sy = sy + mBolckWidth){
                float dst = (sx - cx) * (sx - cx) + (sy - cy) * (sy - cy);
                if(dst < maxDst){
                    if(isDrawGrid){
                        paint.setStyle(Paint.Style.FILL);
                        mCanvas.drawPoint(sx,sy ,paint);
                        mCanvas.drawPoint(sx,sy ,paint);
                        mCanvas.drawPoint(sx,sy ,paint);
                        mCanvas.drawPoint(sx,sy ,paint);
                    }
                    if(sx < offsetLeft)
                        offsetLeft = sx;
                    if(sy < offsetTop)
                        offsetTop = sy;
                }
            }
        }
        offsetLeft = offsetLeft + (GRID_COUNT - (maxX - minX + 1)) / 2 * mBolckWidth;
        offsetTop = offsetTop + (GRID_COUNT - (maxY - minY + 1)) / 2 * mBolckWidth;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(mGestureDetector != null)
            mGestureDetector.onTouchEvent(event);
       if(event.getAction() == MotionEvent.ACTION_UP){
             if(selectIndex == -1)
                 return true;
                 if (isOutView()) {
                     for (int i = 0;i < orginPath.size();i++) {
                         mPaths.get(selectIndex).get(i).x  = orginPath.get(i).x;
                         mPaths.get(selectIndex).get(i).y  = orginPath.get(i).y;
                     }
                 } else {
                     int sx = 0,sy = 0;
                     synchronized (mPaths.get(selectIndex)){
                         for (int i = 0;i<mPaths.get(selectIndex).size();i++){
                             if(i == 0 ){
                                sx = mPaths.get(selectIndex).get(i).x - Math.round(mPaths.get(selectIndex).get(i).x * 1.0f/mBolckWidth)* mBolckWidth;
                                sy = mPaths.get(selectIndex).get(i).y - Math.round(mPaths.get(selectIndex).get(i).y * 1.0f/mBolckWidth)* mBolckWidth;
                             }
                             mPaths.get(selectIndex).get(i).x -= sx;
                             mPaths.get(selectIndex).get(i).y -= sy;
                         }
                         if (isOutView()) {
                             for (int i = 0;i < orginPath.size();i++) {
                                 mPaths.get(selectIndex).get(i).x  = orginPath.get(i).x;
                                 mPaths.get(selectIndex).get(i).y  = orginPath.get(i).y;
                             }
                         }
                     }
                 }
             selectIndex = -1;
           if (mOnMoveFinishListener != null) {
               mOnMoveFinishListener.onMoveFinish(mCanvas,mPaths);
           }
         }
         return true;
    }

    private boolean isOutView() {
        float dx;
        float dy;
        for (Point p:mPaths.get(selectIndex)) {
            dx = offsetLeft + p.x - centerX;
            dy = offsetTop + p.y - centerY;
            if(dx * dx + dy * dy > maxDst)
                return true;
        }
        return false;
    }

    private boolean isSelected(List<Point> path, Point pt){
        int minX = 100000,maxX = 0;
        for (Point p : path){
            if(p.x < minX)
                minX = p.x;
            if(p.x > maxX)
                maxX = p.x;
        }
        int nCross = 0;// 定义变量，统计目标点向右画射线与多边形相交次数
        for (int i = 0; i < path.size(); i++) { //遍历多边形每一个节点
            Point p1;
            Point p2;
            if(path.get(i).y >= path.get((i+1)%path.size()).y){
                p1= new Point((int)(offsetLeft+path.get((i+1)%path.size()).x),(int)(offsetTop+path.get((i+1)%path.size()).y));
                p2= new Point((int)(offsetLeft+path.get(i).x),(int)(offsetTop+path.get(i).y));
            }else {
                p1= new Point((int)(offsetLeft+path.get(i).x),(int)(offsetTop+path.get(i).y));
                p2= new Point((int)(offsetLeft+path.get((i+1)%path.size()).x),(int)(offsetTop+path.get((i+1)%path.size()).y));
            }
            // p1是这个节点，p2是下一个节点，两点连线是多边形的一条边
            // 以下算法先以y轴坐标来判断的
            if ( p1.y == p2.y )continue;//如果这条边是水平的，跳过
            if ( pt.y <  p1.y) continue;//如果目标点高于这个线段，跳过
            if ( pt.y >  p2.y) continue;//如果目标点低于这个线段，跳过
            if (pt.x < minX) continue;//如果目标点在图形左边，跳过
            if (pt.x > maxX) continue;//如果目标点在图形右边，跳过
            //那么下面的情况就是：如果过p1画水平线，过p2画水平线，目标点在这两条线中间
            int x = (pt.y - p1.y ) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
            // 这段的几何意义是 过目标点，画一条水平线，x是这条线与多边形当前边的交点x坐标
            if ( x > pt.x ) nCross++; //如果交点在右边，统计加一。这等于从目标点向右发一条射线（ray），与多边形各边的相交（crossing）次数
        }
        if (nCross % 2 == 1) {
            return true; //如果是奇数，说明在多边形里
        } else {
            return false; //否则在多边形外 或 边上
        }
    }

    @Override
    public boolean onDown(MotionEvent e) {
        //遍历多边形每一个节点
        for (int i = 0; i < mPaths.size(); i++) {
            if (isSelected(mPaths.get(i),new Point((int)e.getX(),(int)e.getY()))) {
                selectIndex = i;
                orginPath = new ArrayList<>();
                for (Point p:mPaths.get(i)) {
                    orginPath.add(new Point(p.x,p.y));
                }
                return true;
            }else
                selectIndex = -1;
        }
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent start, MotionEvent end, float dX, float dY) {
        if(selectIndex == -1)
            return false;
        for (Point p:mPaths.get(selectIndex)) {
            float dx = offsetLeft + p.x - dX - centerX;
            float dy = offsetTop + p.y - dY - centerY;
            if(p.x - dX < getLeft() || p.y  - dY < getTop()
                    ||  p.x  - dX > getRight() || p.y - dY > getBottom() || dx * dx + dy * dy > maxDst)
                return true;
        }
        synchronized (mPaths.get(selectIndex)){
            for (Point p:mPaths.get(selectIndex)) {
                p.x -= dX;
                p.y -= dY;
            }
        }
        return true;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent start, MotionEvent end, float vX, float vY) {
        return false;
    }
}
